﻿// -------------------------------------------------------------------------------------------------------------------------------
// <copyright file="TestSubtractIndexedCMFlattenDenseMatrix.cs" company="Exul">
//     Copyright (c) Exul. All rights reserved.
// </copyright>
// <author>
//     Exul
// </author>
// -------------------------------------------------------------------------------------------------------------------------------

namespace ExulLibrary.Mathematics.LinearAlgebra.Sequential.Tests.CcsSparseMatrixTest.DoubleExtensions
{
    #region Usings

    using System;
    using System.Globalization;

    using ExulLibrary.Exceptions;
    using ExulLibrary.Mathematics.LinearAlgebra.Exceptions;
    using ExulLibrary.Mathematics.LinearAlgebra.Sequential.DoubleExtensions;

    using NUnit.Framework;

    #endregion

    /// <summary>
    ///     Test suite for
    ///     <see cref="CcsSparseMatrixExtensions.Subtract(CcsSparseMatrix{double},int[],int[],CMFlattenDenseMatrix{double})"/>.
    /// </summary>
    [TestFixture]
    [Category("ExulLibrary.Mathematics.LinearAlgebra.Sequential.DoubleExtensions.CcsSparseMatrixExtensions")]
    public sealed class TestSubtractIndexedCMFlattenDenseMatrix
    {
        /// <summary>
        ///     Tests the method when indexes specify existing elements.
        /// </summary>
        [Test]
        public void WhenMinuendIndexesAreValid()
        {
            // Arrange
            // Matrix:
            // 10(Y) 30 50(Y)
            //  0    40  0
            // 20     0  0
            // 80(Y)  0 70(Y)
            var minuend = new CcsSparseMatrix<double>(
                rowsCount: 4,
                columnsCount: 3,
                columnsBegins: new[] { 0, 3, 5, 7 },
                rowsIndexes: new[] { 0, 2, 3, 0, 1, 0, 3 },
                data: new double[] { 10, 20, 80, 30, 40, 50, 70 });

            var minuendRowsIndexes = new[] { 0, 3 };
            var minuendColumnsIndexes = new[] { 0, 2 };

            var data = new double[]
                       {
                           1, 2,
                           3, 4
                       };
            var subtrahend = new CMFlattenDenseMatrix<double>(
                rowsCount: 2,
                columnsCount: 2,
                data: data);

            // Act
            minuend.Subtract(minuendRowsIndexes, minuendColumnsIndexes, subtrahend);

            // Assert
            Assert.That(minuend.Values, Is.EquivalentTo(new[] { 9, 20, 78, 30, 40, 47, 66 }));
        }

        /// <summary>
        ///     Tests the method when minuendRowsIndexes parameter is empty collection.
        /// </summary>
        [Test]
        public void WhenRowsIndexesCollectionIsEmpty()
        {
            // Arrange
            // Matrix:
            // 10 30 50
            //  0 40  0
            // 20  0  0
            // 80  0 70
            var minuend = new CcsSparseMatrix<double>(
                rowsCount: 4,
                columnsCount: 3,
                columnsBegins: new[] { 0, 3, 5, 7 },
                rowsIndexes: new[] { 0, 2, 3, 0, 1, 0, 3 },
                data: new double[] { 10, 20, 80, 30, 40, 50, 70 });

            var minuendRowsIndexes = new int[0];
            var minuendColumnsIndexes = new[] { 0, 2 };

            var data = new double[]
                       {
                           1, 2,
                           3, 4
                       };
            var subtrahend = new CMFlattenDenseMatrix<double>(
                rowsCount: 2,
                columnsCount: 2,
                data: data);

            // Act
            minuend.Subtract(minuendRowsIndexes, minuendColumnsIndexes, subtrahend);

            // Assert
            Assert.That(minuend.Values, Is.EquivalentTo(new[] { 10, 20, 80, 30, 40, 50, 70 }));
        }

        /// <summary>
        ///     Tests the method when minuendColumnsIndexes parameter is empty collection.
        /// </summary>
        [Test]
        public void WhenColumnsIndexesCollectionIsEmpty()
        {
            // Arrange
            // Matrix:
            // 10 30 50
            //  0 40  0
            // 20  0  0
            // 80  0 70
            var minuend = new CcsSparseMatrix<double>(
                rowsCount: 4,
                columnsCount: 3,
                columnsBegins: new[] { 0, 3, 5, 7 },
                rowsIndexes: new[] { 0, 2, 3, 0, 1, 0, 3 },
                data: new double[] { 10, 20, 80, 30, 40, 50, 70 });

            var minuendRowsIndexes = new[] { 0, 3 };
            var minuendColumnsIndexes = new int[0];

            var data = new double[]
                       {
                           1, 2,
                           3, 4
                       };
            var subtrahend = new CMFlattenDenseMatrix<double>(
                rowsCount: 2,
                columnsCount: 2,
                data: data);

            // Act
            minuend.Subtract(minuendRowsIndexes, minuendColumnsIndexes, subtrahend);

            // Assert
            Assert.That(minuend.Values, Is.EquivalentTo(new[] { 10, 20, 80, 30, 40, 50, 70 }));
        }

#if !SAFE

        /// <summary>
        ///     Tests the method when indexes specify an element not in portrait of the minuend matrix.
        /// </summary>
        [Test]
        public void WhenMinuendIndexesSpecifyNotAnElementInPortrait()
        {
            // Arrange
            // Matrix:
            // 10(Y) 30 50(Y)
            //  0    40  0
            // 20(Y)  0  0(Y)
            // 80     0 70
            var minuend = new CcsSparseMatrix<double>(
                rowsCount: 4,
                columnsCount: 3,
                columnsBegins: new[] { 0, 3, 5, 7 },
                rowsIndexes: new[] { 0, 2, 3, 0, 1, 0, 3 },
                data: new double[] { 10, 20, 80, 30, 40, 50, 70 });

            var minuendRowsIndexes = new[] { 0, 2 };
            var minuendColumnsIndexes = new[] { 0, 2 };

            var data = new double[]
            {
                1, 2,
                3, 4
            };
            var subtrahend = new CMFlattenDenseMatrix<double>(
                rowsCount: 2,
                columnsCount: 2,
                data: data);

            // Act
            minuend.Subtract(minuendRowsIndexes, minuendColumnsIndexes, subtrahend);

            // Assert
            Assert.That(minuend.Values, Is.EquivalentTo(new[] { 9, 18, 80, 30, 40, 47, 70 }));
        }

#endif

#if SAFE

        /// <summary>
        ///     Tests the method when indexes specify an element not in portrait of the minuend matrix.
        /// </summary>
        [Test]
        public void WhenMinuendIndexesSpecifyNotAnElementInPortrait()
        {
            // Arrange
            // Matrix:
            // 10(Y) 30 50(Y)
            //  0    40  0
            // 20(Y)  0  0(Y)
            // 80     0 70
            var minuend = new CcsSparseMatrix<double>(
                rowsCount: 4,
                columnsCount: 3,
                columnsBegins: new[] { 0, 3, 5, 7 },
                rowsIndexes: new[] { 0, 2, 3, 0, 1, 0, 3 },
                data: new double[] { 10, 20, 80, 30, 40, 50, 70 });

            var minuendRowsIndexes = new[] { 0, 2 };
            var minuendColumnsIndexes = new[] { 0, 2 };

            var data = new double[]
                       {
                           1, 2,
                           3, 4
                       };
            var subtrahend = new CMFlattenDenseMatrix<double>(
                rowsCount: 2,
                columnsCount: 2,
                data: data);

            // Act and Assert
            Assert.That(
                () => minuend.Subtract(minuendRowsIndexes, minuendColumnsIndexes, subtrahend),
                Throws.TypeOf<ReadOnlyMatrixElementException>().With.Property("ParamName").EqualTo("minuend"));
        }

        /// <summary>
        ///     Tests the method when minuend parameter is <see langword="null"/>.
        /// </summary>
        [Test]
        public void WhenMinuendIsNull()
        {
            var minuendRowsIndexes = new[] { 0, 3 };
            var minuendColumnsIndexes = new[] { 0, 2 };

            var data = new double[]
                       {
                           1, 2,
                           3, 4
                       };
            var subtrahend = new CMFlattenDenseMatrix<double>(
                rowsCount: 2,
                columnsCount: 2,
                data: data);

            // Act and Assert
            Assert.That(
                () => CcsSparseMatrixExtensions.Subtract(null, minuendRowsIndexes, minuendColumnsIndexes, subtrahend),
                Throws.TypeOf<ArgumentNullException>().With.Property("ParamName").EqualTo("minuend"));
        }

        /// <summary>
        ///     Tests the method when subtrahend parameter is <see langword="null"/>.
        /// </summary>
        [Test]
        public void WhenSubtrahendIsNull()
        {
            // Arrange
            // Matrix:
            // 10 30 50
            //  0 40  0
            // 20  0  0
            // 80  0 70
            var minuend = new CcsSparseMatrix<double>(
                rowsCount: 4,
                columnsCount: 3,
                columnsBegins: new[] { 0, 3, 5, 7 },
                rowsIndexes: new[] { 0, 2, 3, 0, 1, 0, 3 },
                data: new double[] { 10, 20, 80, 30, 40, 50, 70 });

            var minuendRowsIndexes = new[] { 0, 3 };
            var minuendColumnsIndexes = new[] { 0, 2 };

            // Act and Assert
            Assert.That(
                () => minuend.Subtract(minuendRowsIndexes, minuendColumnsIndexes, (CMFlattenDenseMatrix<double>)null),
                Throws.TypeOf<ArgumentNullException>().With.Property("ParamName").EqualTo("subtrahend"));
        }

        /// <summary>
        ///     Tests the method when minuendRowsIndexes parameter is <see langword="null"/>.
        /// </summary>
        [Test]
        public void WhenMinuendRowsIndexesCollectionIsNull()
        {
            // Arrange
            // Matrix:
            // 10 30 50
            //  0 40  0
            // 20  0  0
            // 80  0 70
            var minuend = new CcsSparseMatrix<double>(
                rowsCount: 4,
                columnsCount: 3,
                columnsBegins: new[] { 0, 3, 5, 7 },
                rowsIndexes: new[] { 0, 2, 3, 0, 1, 0, 3 },
                data: new double[] { 10, 20, 80, 30, 40, 50, 70 });

            var minuendColumnsIndexes = new[] { 0, 2 };

            var data = new double[]
                       {
                           1, 2,
                           3, 4
                       };
            var subtrahend = new CMFlattenDenseMatrix<double>(
                rowsCount: 2,
                columnsCount: 2,
                data: data);

            // Act and Assert
            Assert.That(
                () => minuend.Subtract(null, minuendColumnsIndexes, subtrahend),
                Throws.TypeOf<ArgumentNullException>().With.Property("ParamName").EqualTo("minuendRowsIndexes"));
        }

        /// <summary>
        ///     Tests the method when minuendColumnsIndexes parameter is <see langword="null"/>.
        /// </summary>
        [Test]
        public void WhenAugendColumnsIndexesCollectionIsNull()
        {
            // Arrange
            // Matrix:
            // 10 30 50
            //  0 40  0
            // 20  0  0
            // 80  0 70
            var minuend = new CcsSparseMatrix<double>(
                rowsCount: 4,
                columnsCount: 3,
                columnsBegins: new[] { 0, 3, 5, 7 },
                rowsIndexes: new[] { 0, 2, 3, 0, 1, 0, 3 },
                data: new double[] { 10, 20, 80, 30, 40, 50, 70 });

            var minuendRowsIndexes = new[] { 0, 3 };

            var data = new double[]
                       {
                           1, 2,
                           3, 4
                       };
            var subtrahend = new CMFlattenDenseMatrix<double>(
                rowsCount: 2,
                columnsCount: 2,
                data: data);

            // Act and Assert
            Assert.That(
                () => minuend.Subtract(minuendRowsIndexes, null, subtrahend),
                Throws.TypeOf<ArgumentNullException>().With.Property("ParamName").EqualTo("minuendColumnsIndexes"));
        }

        /// <summary>
        ///     Tests the method when an element of minuendRowsIndexes collection is negative or greater than or equal to
        ///     the number of rows of the minuend matrix.
        /// </summary>
        /// <param name="rowIndex">
        ///     The row index.
        /// </param>
        /// <param name="location">
        ///     The location of the index in the storage.
        /// </param>
        [Test]
        public void WhenRowIndexIsOutOfRange([Values(-1, 4)] int rowIndex, [Values(0, 1)] int location)
        {
            // Arrange
            // Matrix:
            // 10 30 50
            //  0 40  0
            // 20  0  0
            // 80  0 70
            var minuend = new CcsSparseMatrix<double>(
                rowsCount: 4,
                columnsCount: 3,
                columnsBegins: new[] { 0, 3, 5, 7 },
                rowsIndexes: new[] { 0, 2, 3, 0, 1, 0, 3 },
                data: new double[] { 10, 20, 80, 30, 40, 50, 70 });

            var minuendRowsIndexes = new int[2];
            minuendRowsIndexes[location] = rowIndex;

            var minuendColumnsIndexes = new[] { 0, 2 };

            var data = new double[]
                       {
                           1, 2,
                           3, 4
                       };
            var subtrahend = new CMFlattenDenseMatrix<double>(
                rowsCount: 2,
                columnsCount: 2,
                data: data);

            // Act and Assert
            var argumentName = string.Format(CultureInfo.InvariantCulture, "minuendRowsIndexes[{0}]", location);
            Assert.That(
                () => minuend.Subtract(minuendRowsIndexes, minuendColumnsIndexes, subtrahend),
                Throws.TypeOf<ElementIndexIsOutOfRangeException>().With.Property("ParamName").EqualTo(argumentName));
        }

        /// <summary>
        ///     Tests the method when an element of minuendColumnsIndexes collection is negative or greater than or equal to
        ///     the number of columns of the minuend matrix.
        /// </summary>
        /// <param name="columnIndex">
        ///     The column index.
        /// </param>
        /// <param name="location">
        ///     The location of the index in the storage.
        /// </param>
        [Test]
        public void WhenColumnIndexIsOutOfRange([Values(-1, 3)] int columnIndex, [Values(0, 1)] int location)
        {
            // Arrange
            // Matrix:
            // 10 30 50
            //  0 40  0
            // 20  0  0
            // 80  0 70
            var minuend = new CcsSparseMatrix<double>(
                rowsCount: 4,
                columnsCount: 3,
                columnsBegins: new[] { 0, 3, 5, 7 },
                rowsIndexes: new[] { 0, 2, 3, 0, 1, 0, 3 },
                data: new double[] { 10, 20, 80, 30, 40, 50, 70 });

            var minuendRowsIndexes = new[] { 0, 3 };
            var minuendColumnsIndexes = new int[2];
            minuendColumnsIndexes[location] = columnIndex;

            var data = new double[]
                       {
                           1, 2,
                           3, 4
                       };
            var subtrahend = new CMFlattenDenseMatrix<double>(
                rowsCount: 2,
                columnsCount: 2,
                data: data);

            // Act and Assert
            var argumentName = string.Format(CultureInfo.InvariantCulture, "minuendColumnsIndexes[{0}]", location);
            Assert.That(
                () => minuend.Subtract(minuendRowsIndexes, minuendColumnsIndexes, subtrahend),
                Throws.TypeOf<ElementIndexIsOutOfRangeException>().With.Property("ParamName").EqualTo(argumentName));
        }

#endif
    }
}